﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace RobotArm
{
    public partial class Form1 : Form
    {
        private long recvCount = 0; // 接收字节数计数
        private long sendCount = 0; // 发送字节数计数
        private StringBuilder stringBuilder = new StringBuilder(); // 用于构建接收的串口数据
        private DateTime currentTime; // 用于获取接受串口数据时的时间
        private Regex rgx = new Regex(@"\s"); // 串口使用 HEX 方式发送数据时的正则表达式

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            initUI();
        }

        /// <summary>
        /// 初始化 UI 显示
        /// </summary>
        private void initUI()
        {
            initSerialConfigure();
            initRecvAndSendConfigure();
            buttonSend.Enabled = true;
            numericUpDownInterval.Enabled = !checkBoxAutoSend.Checked;
        }

        /// <summary>
        /// 初始化串口设置的配置
        /// </summary>
        private void initSerialConfigure()
        {
            refreshSerialPortName();

            string[] baud = { "43000", "56000", "57600", "115200", "128000", "230400", "256000", "460800" };
            comboBoxBaud.Items.AddRange(baud);
            comboBoxBaud.Text = baud[0];

            string[] dataBit = { "8", "9" };
            comboBoxDataBit.Items.AddRange(dataBit);
            comboBoxDataBit.Text = dataBit[0];

            string[] varifyBit = { "None", "Odd", "Even", "Mark", "Space" };
            comboBoxVarifyBit.Items.AddRange(varifyBit);
            comboBoxVarifyBit.Text = varifyBit[0];

            string[] stopBit = { "1", "1.5", "2" };
            comboBoxStopBit.Items.AddRange(stopBit);
            comboBoxStopBit.Text = stopBit[0];
        }

        /// <summary>
        /// 初始化接收和发送控制的配置
        /// </summary>
        private void initRecvAndSendConfigure()
        {
            radioButtonRecvASCII.Checked = true;
            radioButtonSendASCII.Checked = true;
        }

        /// <summary>
        /// 刷新可用串口列表
        /// </summary>
        private void refreshSerialPortName()
        {
            try
            {
                comboBoxSerialPort.Items.Clear();

                string[] serialPortNames = System.IO.Ports.SerialPort.GetPortNames();
                comboBoxSerialPort.Items.AddRange(serialPortNames);

                comboBoxSerialPort.Text = serialPortNames.Length > 0 ? serialPortNames[0] : "";
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 设置所有有关串口设置的 combobox 的可用状态
        /// </summary>
        /// <param name="enable">可用状态</param>
        private void setAllSerialConfiguerComboBoxEnable(bool enable)
        {
            comboBoxSerialPort.Enabled = enable;
            comboBoxBaud.Enabled       = enable;
            comboBoxDataBit.Enabled    = enable;
            comboBoxVarifyBit.Enabled  = enable;
            comboBoxStopBit.Enabled    = enable;
        }

        /// <summary>
        /// 通过 comboboxVarifyBit 内容设置串口的校验位
        /// </summary>
        private void setSerialParity()
        {
            System.IO.Ports.Parity parity;
            switch (comboBoxVarifyBit.Text)
            {
                case "None":
                    parity = System.IO.Ports.Parity.None;
                break;

                case "Odd":
                    parity = System.IO.Ports.Parity.Odd;
                break;

                case "Even":
                    parity = System.IO.Ports.Parity.Even;
                break;

                case "Mark":
                    parity = System.IO.Ports.Parity.Mark;
                break;

                case "Space":
                    parity = System.IO.Ports.Parity.Space;
                break;

                default:
                    goto case "None";
            }
            serialPort.Parity = parity;
        }

        /// <summary>
        /// 通过 comboboxStopBit 内容设置串口的停止位
        /// </summary>
        private void setSerialStopBit()
        {
            switch (comboBoxStopBit.Text)
            {
                case "1":
                    serialPort.StopBits = System.IO.Ports.StopBits.One;
                break;

                case "1.5":
                    serialPort.StopBits = System.IO.Ports.StopBits.OnePointFive;
                break;

                case "2":
                    serialPort.StopBits = System.IO.Ports.StopBits.Two;
                break;

                default:
                    goto case "1";
            }
        }

        /// <summary>
        /// 通过各个 combobox 配置串口
        /// </summary>
        private void configSerialPort()
        {
            serialPort.PortName = comboBoxSerialPort.Text;
            serialPort.BaudRate = Convert.ToInt32(comboBoxBaud.Text);
            serialPort.DataBits = Convert.ToInt32(comboBoxDataBit.Text);
            setSerialParity();
            setSerialStopBit();
        }

        /// <summary>
        /// 统一处理串口异常
        /// </summary>
        /// <param name="ex">出现的异常</param>
        private void handleSerialException(Exception ex)
        {
            System.Media.SystemSounds.Beep.Play();
            MessageBox.Show(ex.Message);

            serialPort = new System.IO.Ports.SerialPort();
            refreshSerialPortName();

            buttonSerialPortSwitch.Text = "打开串口";
            setAllSerialConfiguerComboBoxEnable(true);

            buttonSend.Enabled = false;
        }

        private void buttonSerialPortSwitch_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();

                    setAllSerialConfiguerComboBoxEnable(true);
                    textBoxRecv.Clear();
                    textBoxSend.Clear();
                    buttonSerialPortSwitch.Text = "打开串口";
                    labelStatus.Text = "串口已关闭";
                    labelStatus.ForeColor = Color.Red;
                    buttonSend.Enabled = false;
                }
                else
                {
                    configSerialPort();
                    serialPort.Open();

                    setAllSerialConfiguerComboBoxEnable(false);
                    buttonSerialPortSwitch.Text = "关闭串口";
                    labelStatus.Text = "串口已打开";
                    labelStatus.ForeColor = Color.Green;
                    buttonSend.Enabled = true;
                }
            }
            catch (Exception ex)
            {
                handleSerialException(ex);
            }
        }

        private void comboBoxSerialPort_Click(object sender, EventArgs e)
        {
            refreshSerialPortName();
        }

        private void buttonSend_Click(object sender, EventArgs e)
        {
            if (!serialPort.IsOpen)
            {
                return;
            }

            try
            {
                int count = 0;

                if (radioButtonSendASCII.Checked) // 以 ASCII 方式发送
                {
                    count = textBoxSend.Text.Length;
                    serialPort.Write(textBoxSend.Text);
                }
                else // 以 HEX 方式发送
                {
                    // 去除所有空格
                    string str = rgx.Replace(textBoxSend.Text, "");
                    count = (str.Length + 1) / 2; // 计算出发送的字节数，+1 是为了向上取整
                    byte[] tmp = new byte[count]; // 要发送的字节数组

                    try
                    {
                        int loopCnt = str.Length / 2; // 处理成对的字符的循环次数
                        for (int i = 0; i < loopCnt; i++)
                        {
                            tmp[i] = Convert.ToByte(str.Substring(i * 2, 2), 16);
                        }

                        // 假如字符个数为奇数则处理最后一个字符
                        if (str.Length % 2 != 0)
                        {
                            tmp[count - 1] = Convert.ToByte(str.Substring(str.Length - 1, 1), 16);
                        }

                        serialPort.Write(tmp, 0, count);
                    }
                    // 处理字符转换成字节的异常
                    catch (Exception ex) when (ex is FormatException || ex is OverflowException)
                    {
                        System.Media.SystemSounds.Beep.Play();
                        MessageBox.Show(ex.Message);

                        return;
                    }
                }

                // 检查是否要发送新行
                if (checkBoxSendNewLine.Checked)
                {
                    count += 1;
                    serialPort.WriteLine("");
                }

                sendCount += count;
                labelTxCount.Text = sendCount.ToString() + " Bytes";
            }
            catch (Exception ex)
            {
                handleSerialException(ex);
            }
        }

        private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            int count = serialPort.BytesToRead;
            byte[] recvBuf = new byte[count];
            recvCount += count;
            serialPort.Read(recvBuf, 0, count);

            stringBuilder.Clear();

            // 添加接收时间
            if (checkBoxShowRecvTime.Checked)
            {
                currentTime = DateTime.Now;
                stringBuilder.Append(currentTime.ToString("HH:mm:ss") + "  ");
            }

            // 通过指定接收格式获取数据
            if (radioButtonRecvASCII.Checked)
            {
                stringBuilder.Append(Encoding.ASCII.GetString(recvBuf));
            }
            else
            {
                foreach (byte b in recvBuf)
                {
                    // 转换为两位的 16 进制字符串
                    stringBuilder.Append(b.ToString("X2") + ' ');
                }
            }

            // 委托修改 UI 显示
            try
            {
                this.Invoke((EventHandler)delegate
                {
                    // 为了使得时间显示在一行的最前面，在接收区最后添加一个回车
                    if (checkBoxShowRecvTime.Checked && !textBoxRecv.Text.EndsWith("\n"))
                    {
                        textBoxRecv.AppendText("\n");
                    }

                    textBoxRecv.AppendText(stringBuilder.ToString());
                    labelRxCount.Text = recvCount.ToString() + " Bytes";
                });
            }
            catch (Exception ex)
            {
                System.Media.SystemSounds.Beep.Play();
                MessageBox.Show(ex.Message);
            }
        }

        private void buttonClearRecv_Click(object sender, EventArgs e)
        {
            textBoxRecv.Clear();
            recvCount = 0;
            labelRxCount.Text = recvCount.ToString() + " Bytes";
        }

        private void checkBoxAutoSend_CheckedChanged(object sender, EventArgs e)
        {
            numericUpDownInterval.Enabled = !checkBoxAutoSend.Checked;

            if (checkBoxAutoSend.Checked)
            {
                timer.Interval = (int)numericUpDownInterval.Value;
                timer.Start();
                labelStatus.Text = "串口已打开，自动发送中...";
            }
            else
            {
                timer.Stop();
                labelStatus.Text = "串口已打开";
            }
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            // 假如发送区只有空字符（包括空格等）则不发送数据，且清空发送区
            if (textBoxSend.Text.Trim().Equals(""))
            {
                textBoxSend.Text = "";
            }
            // 假如发送区不为空，则发送数据
            else
            {
                buttonSend_Click(buttonSend, new EventArgs());
            }
        }
    }
}
